package com.yupi.shortchain.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.shortchain.common.DeleteRequest;
import com.yupi.shortchain.common.ErrorCode;
import com.yupi.shortchain.config.DistributedIdGenerator;
import com.yupi.shortchain.exception.BusinessException;
import com.yupi.shortchain.exception.ThrowUtils;
import com.yupi.shortchain.mapper.UserMapper;
import com.yupi.shortchain.model.dto.urlrelate.UrlRelateAddRequest;
import com.yupi.shortchain.model.dto.urlrelate.UrlRelateUpdateRequest;
import com.yupi.shortchain.model.entity.UrlRelate;
import com.yupi.shortchain.model.entity.User;
import com.yupi.shortchain.service.UrlRelateService;
import com.yupi.shortchain.mapper.UrlRelateMapper;
import com.yupi.shortchain.service.UrlTagService;
import com.yupi.shortchain.service.UserService;
import com.yupi.shortchain.utils.Base62Converter;
import com.yupi.shortchain.utils.NetUtils;
import lombok.extern.slf4j.Slf4j;
import net.openhft.hashing.LongHashFunction;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RMap;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import static com.yupi.shortchain.constant.RedissonConstants.SHORT_LINK_BLOOM_FILTER_KEY;
import static com.yupi.shortchain.constant.RedissonConstants.SHORT_LINK_CACHE_MAP_KEY;
import static com.yupi.shortchain.constant.ShortLinkConstant.SHORT_LINK;

/**
* @author liu
* @description 针对表【url_relate(链接关系表)】的数据库操作Service实现
* @createDate 2024-12-04 08:23:48
*/
@Service
@Slf4j
public class UrlRelateServiceImpl extends ServiceImpl<UrlRelateMapper, UrlRelate>
    implements UrlRelateService{
    @Resource
    private UserService userService;

    @Resource
    private Redisson redisson;

    @Resource
    private UrlTagService urlTagService;

    @Resource
    private DistributedIdGenerator distributedIdGenerator;

    @Resource
    private UserMapper userMapper;

    @Resource
    private UrlRelateMapper urlRelateMapper;


    private String createShortUrl(String longUrl) {

        String shortUrlStr64 = getShortUrlBySha256(longUrl);
        //使用布隆过滤器校验
        RBloomFilter<String> bloomFilter = redisson.getBloomFilter(SHORT_LINK_BLOOM_FILTER_KEY);
        //初始化布隆过滤器：预计元素为100000000L,误差率为3%
        bloomFilter.tryInit(100000000L, 0.03);

        if (!bloomFilter.contains(shortUrlStr64)) {
            //hash冲突重新生成 在结尾重新添加一个分布式id
            shortUrlStr64 = shortUrlStr64 + distributedIdGenerator.generateDistributedId(SHORT_LINK);
        }
        bloomFilter.add(shortUrlStr64);
        return shortUrlStr64;
    }

    private static String getShortUrlBySha256(String longUrl) {
        // 获取 MurmurHash3 的实例
        LongHashFunction murmur3 = LongHashFunction.murmur_3();

        // 计算输入字符串的哈希值
        long hash = murmur3.hashChars(longUrl);

        return Base62Converter.toBase62(hash);
    }

    @Override
    public Long addUrlRelate(UrlRelateAddRequest urlRelateAddRequest) {
        User loginUser = userService.getLoginUser();
        // 获取长链
        String longUrl = urlRelateAddRequest.getLongUrl();
        synchronized (longUrl.intern()){
            UrlRelate urlRelate = new UrlRelate();
            urlRelate.setLongUrl(longUrl);
            // 校验长链
            this.validUrlRelate(urlRelate,true);
            // 该长链不存在，生成短链
            String shortUrl = this.createShortUrl(longUrl);
            log.info("=======短链====== {}",shortUrl);
            urlRelate.setSortUrl(shortUrl);
            //自动解析获取 title
            setUrlTitleAndImg(longUrl,urlRelate);
            urlRelate.setUserId(loginUser.getId());
            //保存到数据库
            boolean res = this.save(urlRelate);
            if(!res){
                throw new BusinessException(ErrorCode.OPERATION_ERROR);
            }
            // 返回该记录的id
            return urlRelate.getId();
        }
    }

    @Override
    public String addUrlRelate(String longUrl) {
        User loginUser = userService.getLoginUser();
        String shortUrl = null;
        synchronized (longUrl.intern()){
            UrlRelate urlRelate = new UrlRelate();
            urlRelate.setLongUrl(longUrl);
            // 校验长链
            this.validUrlRelate(urlRelate,true);
            // 该长链不存在，生成短链
            shortUrl = this.createShortUrl(longUrl);
            log.info("=======短链====== {}",shortUrl);
        }
        return shortUrl;
    }

    @Override
    public void validUrlRelate(UrlRelate urlRelate, boolean add) {
        if(urlRelate == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String longUrl = urlRelate.getLongUrl();
        //add==true 表示这是一个创建操作
        //add == false 这是一个更新操作
        if(add){
            // 判断 longUrl 是否为空 或者 空白字符
            ThrowUtils.throwIf(StringUtils.isAnyBlank(longUrl),ErrorCode.PARAMS_ERROR);
            // 校验长链是否存在
            QueryWrapper<UrlRelate> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(UrlRelate::getLongUrl, longUrl);
            queryWrapper.lambda().eq(UrlRelate::getUserId, StpUtil.getLoginId());
            UrlRelate selectedOne = urlRelateMapper.selectOne(queryWrapper);
            if(selectedOne != null){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"长链已存在");
            }
            NetUtils.validateLink(longUrl);
        }
    }

    @Override
    public UrlRelate getLongLink(String shortUrl) {
        QueryWrapper<UrlRelate> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sortUrl", shortUrl);
        UrlRelate urlRelate = urlRelateMapper.selectOne(queryWrapper);
        return urlRelate;
    }
    // todo 只允许本人或者管理员删除
    @Override
    public boolean deleteUrlRelate(DeleteRequest deleteRequest) {
        // 获取当前登录用户
        User loginUser = userService.getLoginUser();
        //获取要删除的UrlRelate对象的id
        long id = deleteRequest.getId();
        // 判断是否存在
        UrlRelate oldUrlRelate = this.getById(id);
        // 如果UrlRelate对象不存在，则抛出异常
        ThrowUtils.throwIf(oldUrlRelate == null, ErrorCode.NOT_FOUND_ERROR);

        // 仅本人或管理员可删除
        if (!oldUrlRelate.getUserId().equals(loginUser.getId()) && !userService.isAdmin()) {
            // 如果不是本人或管理员，则抛出没有权限的异常
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 删除指定id的UrlRelate对象，并返回结果
        return this.removeById(id);
    }

    @Override
    public boolean updateUrlRelate(UrlRelateUpdateRequest urlRelateUpdateRequest) {
        // 创建UrlRelate对象
        UrlRelate urlRelate = new UrlRelate();

        // 将urlRelateUpdateRequest对象的属性复制给urlRelate对象
        BeanUtils.copyProperties(urlRelateUpdateRequest, urlRelate);

        // 参数校验
        this.validUrlRelate(urlRelate, false);

        // 获取id属性的值
        long id = urlRelateUpdateRequest.getId();

        // 根据id获取UrlRelate对象
        UrlRelate oldUrlRelate = this.getById(id);

        // 如果获取到的UrlRelate对象为空，则抛出异常，错误码为NOT_FOUND_ERROR
        ThrowUtils.throwIf(oldUrlRelate == null, ErrorCode.NOT_FOUND_ERROR);

        //移除缓存
        RMap<String, UrlRelate> shortLinkMap = redisson.getMap(SHORT_LINK_CACHE_MAP_KEY);
        shortLinkMap.remove(oldUrlRelate.getSortUrl());
        // 更新urlRelate对象到数据库
        return this.updateById(urlRelate);
    }

    public static void setUrlTitleAndImg(String url, UrlRelate urlRelate) {
        //通过url解析网址title和logo
        String title;
        try {
            //还是一样先从一个URL加载一个Document对象。
            Document doc = Jsoup.connect(url).get();
            Elements links = doc.select("head");
            Elements titleLinks = links.get(0).select("title");
            title = titleLinks.get(0).text();
            log.info("成功解析网站标题============={}", title);
            if (CharSequenceUtil.isBlank(urlRelate.getTitle())) {
                urlRelate.setTitle(title);
            }
            Element faviconLink = doc.head().select("link[href~=.*\\.(ico|png)]").first();
            String faviconUrl = faviconLink != null ? faviconLink.attr("href") : "";

            // Sometimes the favicon URL is relative, so we might need to make it absolute
            if (!(faviconUrl.toLowerCase().startsWith("https") || faviconUrl.toLowerCase().startsWith("http"))) {
                faviconUrl = url + faviconUrl;
            }
            log.info("网站icon============={}", faviconUrl);
            if (CharSequenceUtil.isBlank(urlRelate.getUrlImg())) {
                urlRelate.setUrlImg(faviconUrl);
            }

        } catch (Exception e) {
            log.error("通过url={} 网址解析title和logo 异常：{}", url, e.getMessage());
        }
    }

    // todo 批量生成短链 并且批量保存数据库

}




